﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using Excel = Microsoft.Office.Interop.Excel;
using ExcelDna.Integration;
using FinPlusAssembler;
using FinPlusInterfaces;
using FinPlusCompCore;

namespace FinPlusRTDBooker
{
    public class Booker : FinPlusComponent
    {
        private static Excel.Application _excel = null;
        private static Excel.Workbook _workbook = null;
        private static Excel.Worksheet _worksheet = null;
        private static IConnDatabase _conn = null;
        private static Env _env = Env.unknown;
        private volatile static Config _config;
        private volatile static Object _lock = new Object();
        private static string _user = Environment.UserName;

        //excel  
        [ExcelFunction(Description = "load from db and save to db", Category = "FinPlus.Booking")]
        public static void BookerOpen(string worksheetName, string rangeName)
        {
            try
            {
                Connect();
                var pod = Load(worksheetName, rangeName, _conn);

                if(pod != null) 
                    WindowOpen(pod, _conn);
                
            }
            catch (Exception e)
            {
                Level.Error.Log(e.Message, e);
                throw new BookerException(string.Format("Booker failed to open {0} {1}", worksheetName, rangeName), e);
            }
        }

        //private
        private static void Connect()
        {
            if (_config != null)
                return;

            lock (_lock)
            {
                if (_config != null)
                    return;

                var path = Assembly.GetExecutingAssembly().ConfigPath();
                _config = path.ConfigXml(out _env);
            }

            if (_conn != null)
                return;

            lock (_lock)
            {
                if (_conn != null) 
                    return;
                _conn = IOC.New<IConnDatabase>();
            }
        }

        private static void WindowOpen(IPod value, IConnDatabase conn)
        {
            try
            {
                IOC.Singleton<IWindowHelper>().OpenAsync(Paths.Values["FolderSubViews"] + "PodEditorBooker", "PodEditorBooker", new object[,] { { "Pod", value }, { "Conn", conn } }, _env, ShowType.BackGround);
            }
            catch (Exception e)
            {
                Level.Error.Log(e.Message, e);
                throw new BookerException("Booker window failed to open ", e);
            }
        }

        private static IPod Load(string worksheet, string range, IConnDatabase conn)
        {
            try
            {
                var r = Range(worksheet, range);
                string formula = r.Formula;
                string name = Regex.Match(formula, @"(?<=\=).*(?=\()").Value;
                string parms = Regex.Match(formula, @"(?<=\().*(?=\))").Value;

                if (name == "FinPlusQry") 
                    return Query(worksheet, range, conn, name, parms);//return saved 

                return Parse(worksheet, range, conn, name, parms);//parse new 
            }
            catch (BookerException e)
            {
                Level.Error.Log(e.Message, e);
                return null;
            }
        }

        private static IPod Query(string worksheet, string range, IConnDatabase conn, string name, string parms)
        {
            var r = RangeValue(worksheet, range);
            var ps = parms.Split(',');
            int id = -1;
            if (int.TryParse(r, out id) && ps.Length > 1)
            {
                var q = conn.SelectId(ps[1].ToString().Replace("\"", string.Empty), id);
                q.Execute();
                foreach (var f in q.Values.Values) return f;
            }
            return null;
        }

        private static IPod Parse(string worksheet, string range, IConnDatabase conn, string name, string parms)
        {
            var pod = IOC.New<IPod>(name);
            var nameInfo = typeof(FinPlusDNA.FinPlusDNA).GetMethod(name);
            var index = 0;
            var tempBean = IOC.New<IBean>();
           
            foreach (var parm in parms.Split(','))
            {
                var p = ParamNameAndType(nameInfo, index);
                var bean = tempBean.Clone(index + 4, -1, p[0], RangeValue(worksheet, parm), IOC.FindType(p[1]), DateTime.MinValue, _user);
                pod.TryAdd(bean);
                index++;
            }
            return pod; 
        }

        private static Excel.Range Range(string worksheet, string range)
        {
            if (_excel == null)
            {
                lock (_lock)
                {
                    if (_excel == null)
                    {
                        _excel = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
                        _workbook = _excel.ActiveWorkbook;
                        _worksheet = _workbook.Worksheets[worksheet];
                    }
                }
            }
          
            return (Excel.Range)_worksheet.Range[range];
        }

        private static string RangeValue(string worksheet, string range)
        {
            //if not range then value
            try
            {
                return Range(worksheet, range).Value2.ToString();
            }
            catch
            {
                return range.Replace("\"", string.Empty).TrimStart();
            }
        }

        private static string[] ParamNameAndType(MethodInfo method, int index)
        {
            string[] retVal = {string.Empty,string.Empty};
            if (method != null && method.GetParameters().Length > index)
            {
                var p = method.GetParameters()[index];
                retVal[0] = p.Name;
                retVal[1] = p.ParameterType.FullName;
            }
            return retVal;
        }
    }
}
